#include "libcomm.h"

ProcessSemaphore::ProcessSemaphore(const char* semName, unsigned int nInitValue) 
#ifdef __GNUC__
	:m_sem(SEM_FAILED),m_name(semName)
#endif
{
	MYLIB_ASSERT_NOTNULL(semName);
#ifdef COMM_MSVC
	MYLIB_ASSERT_ERRNO(NULL != (m_sem = CreateSemaphoreA(NULL,nInitValue,SEM_VALUE_MAX,semName)));
#else
	Open(nInitValue);
#endif
}

ProcessSemaphore::~ProcessSemaphore(void)
{
#ifdef COMM_MSVC
	if(NULL != m_sem)
	{
		CloseHandle(m_sem);
		m_sem = NULL;
	}
#else
	Close();
#endif
}

#ifdef __GNUC__

bool ProcessSemaphore::Open(unsigned int nInitValue)
{
	int flag = O_RDWR | O_CREAT | O_EXCL;
	mode_t mode = (S_IRUSR|S_IWUSR)|(S_IRGRP|S_IWGRP)|(S_IROTH|S_IWOTH);//S_IRWXO | S_IRWXG | S_IRWXU;
	mode_t mask_old = umask(0); // do not mask write mode. then other user can to write.
	while (true)
	{		
		m_sem = sem_open((char*)m_name, flag, mode, nInitValue);
		if(SEM_FAILED == m_sem)
		{
			if(EEXIST == errno)
			{
				flag = O_RDWR;
				continue;
			}
			LOGFUNCERROR(sem_open);
			break;
		}
		break;
	}
	umask(mask_old);
	if(SEM_FAILED == m_sem)
		return false;

	if(flag & O_CREAT)
		LOGWARN("shared semaphore \"%s\" created.",(char*)m_name);
	else
		LOGWARN("shared semaphore \"%s\" opened.",(char*)m_name);

	return true;
}

void ProcessSemaphore::Close()
{
	if(SEM_FAILED != m_sem)
	{
		MYLIB_ASSERT_ERRNO(0 == sem_close(m_sem));
		m_sem = SEM_FAILED;
		sem_unlink((char*)m_name);
		LOGWARN("shared semaphore \"%s\" closed.",(char*)m_name);
	}
}

#endif//__GNUC__

void ProcessSemaphore::SetSignal()
{
#ifdef COMM_MSVC
	MYLIB_ASSERT_ERRNO(ReleaseSemaphore(m_sem,1,NULL));
#else
	MYLIB_ASSERT(SEM_FAILED != m_sem);
	MYLIB_ASSERT_ERRNO(0 == sem_post(m_sem));
#endif
}

#ifdef __GNUC__
static void __wait_cleanup(void *arg)
{    
	LOGWARN("thread(%d) was canceled while ProcessSemaphore::WaitSignal().",Thread::Id());
}
#endif

bool ProcessSemaphore::WaitSignal(unsigned int timeoutMs)
{
#ifdef COMM_MSVC

	DWORD dwEvent = WaitForSingleObject(m_sem,timeoutMs); 
	switch (dwEvent) 
	{ 
	case WAIT_OBJECT_0: 
		return true;
	case WAIT_TIMEOUT: 
		return false;
	default: 
		break;
	}
	LOGFUNCERROR(WaitForSingleObject);
	return false;

#else

	MYLIB_ASSERT(SEM_FAILED != m_sem);
	int ret = 0;
	timespec ts = { 0 };
	if (timeoutMs > 0)
		ts = Timer::MakeTimespec(timeoutMs);
	pthread_cleanup_push(__wait_cleanup, NULL);
	while(true)
	{
		int ret;
		if (-1 == timeoutMs)
		{
			ret = sem_wait(m_sem);
		}
		else if (0 == timeoutMs)
		{
			ret = sem_trywait(m_sem);
		}
		else
		{
			ret = sem_timedwait(m_sem, &ts);
		}

		if (0 != ret && EINTR == errno)
		{
			LOGFUNCERROR(sem_timedwait);
			continue;
		}
		break;
	}
	pthread_cleanup_pop(0);

	if(0 == ret)
		return true;
	if(ETIMEDOUT == errno || EAGAIN == errno)
		return false;

	LOGFUNCERROR(sem_timedwait);
	return false;

#endif

}
